using System;
using System.IO;
using System.Runtime.InteropServices;

namespace FileUtility.FTP.FtpSupport
{
	/// <summary>
	/// A class for managed a Stream of data from an FTP site
	/// </summary>
	public class FtpStream : Stream
	{
		private IntPtr hFile;
		private GenericRights readOrWrite;

		public FtpStream(IntPtr hFile, GenericRights readOrWrite)
		{
			this.hFile = hFile;
			this.readOrWrite = readOrWrite;
		}

		~FtpStream()
		{
			this.Close();
		}

		public override void Close()
		{
			if(hFile != IntPtr.Zero)
			{
				NativeMethods.InternetCloseHandle(hFile);
				hFile = IntPtr.Zero;
			}
			base.Close();

			GC.SuppressFinalize(this);
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			if(!CanRead) throw new NotSupportedException();

			//Pin the byte[] so we can calculate the address to write at
			//this avoids having to make another copy of the data.
			GCHandle gchandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
			IntPtr pBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset);
			int read;
			bool rc = NativeMethods.InternetReadFile(hFile, pBuffer, count, out read);
			gchandle.Free();
			FtpException.THROWONFALSE(rc);
			return read;
		}

		public override bool CanRead
		{
			get
			{
				return readOrWrite == GenericRights.Read;
			}
		}

		public override bool CanSeek
		{
			get
			{
				return false;
			}
		}

		public override bool CanWrite
		{
			get
			{
				return readOrWrite == GenericRights.Write;
			}
		}

		public override long Length
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		public override long Position
		{
			get
			{
				throw new NotSupportedException();
			}
			set
			{
				throw new NotSupportedException();
			}
		}

		public override long Seek(long offset, System.IO.SeekOrigin origin)
		{
			throw new NotSupportedException();
		}

		public override void SetLength(long value)
		{
			throw new NotSupportedException();
		}

		public override void Flush()
		{
		}

		public override void Write(byte[] buffer, int offset, int count)
		{
			if(!CanWrite) throw new NotSupportedException();

			//Pin the byte[] so we can calculate the address to write at
			//this avoids having to make another copy of the data.
			GCHandle gchandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
			IntPtr pBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset);
			int written;
			bool rc = NativeMethods.InternetWriteFile(hFile, pBuffer, count, out written);
			gchandle.Free();
			FtpException.THROWONFALSE(rc);
		}
	}
}
